"""
命令处理器
Command Handler

处理向IoT设备发送命令的统一接口
Unified interface for handling commands sent to IoT devices
"""

import logging
import asyncio
from typing import Dict, Any, Optional, List
from datetime import datetime, timedelta
from enum import Enum

from .device_manager import device_manager
from ..interfaces.device_interface import DeviceInterface
from ..utils.resilience import safe_execute, resilience_manager, retry, rate_limit

logger = logging.getLogger(__name__)


class CommandStatus(Enum):
    """
    命令状态枚举
    Command status enumeration
    """
    PENDING = "pending"      # 待执行 / Pending
    EXECUTING = "executing"  # 执行中 / Executing
    SUCCESS = "success"      # 成功 / Success
    FAILED = "failed"        # 失败 / Failed
    TIMEOUT = "timeout"      # 超时 / Timeout
    CANCELLED = "cancelled"  # 已取消 / Cancelled


class CommandType(Enum):
    """
    命令类型枚举
    Command type enumeration
    """
    LOCATION_REQUEST = "location_request"      # 定位请求 / Location request
    HEALTH_CHECK = "health_check"              # 健康检查 / Health check
    DEVICE_CONFIG = "device_config"            # 设备配置 / Device configuration
    FIRMWARE_UPDATE = "firmware_update"        # 固件更新 / Firmware update
    RESTART_DEVICE = "restart_device"          # 重启设备 / Restart device
    SEND_MESSAGE = "send_message"              # 发送消息 / Send message
    CUSTOM = "custom"                          # 自定义命令 / Custom command


class CommandHandler:
    """
    命令处理器
    Command Handler
    
    负责处理向IoT设备发送的各种命令
    Responsible for handling various commands sent to IoT devices
    """
    
    def __init__(self):
        """初始化命令处理器 / Initialize command handler"""
        self.logger = logging.getLogger(f"{self.__class__.__name__}")
        self.command_queue: Dict[str, Dict[str, Any]] = {}  # 命令队列 / Command queue
        self.command_history: List[Dict[str, Any]] = []     # 命令历史 / Command history
        self.max_history_size = 1000                        # 最大历史记录数 / Maximum history size
        self.max_queue_size = 5000                          # 最大队列大小 / Maximum queue size
        self.default_timeout = 30                           # 默认超时时间（秒）/ Default timeout (seconds)
        
        # 命令统计信息 / Command statistics
        self.command_stats = {
            'total_commands': 0,
            'successful_commands': 0,
            'failed_commands': 0,
            'timeout_commands': 0
        }
    
    async def send_command(self, device_id: str, command_type: CommandType, 
                          command_data: Dict[str, Any], timeout: Optional[int] = None) -> Dict[str, Any]:
        """
        发送命令给设备
        Send command to device
        
        Args:
            device_id (str): 设备ID / Device ID
            command_type (CommandType): 命令类型 / Command type
            command_data (Dict[str, Any]): 命令数据 / Command data
            timeout (Optional[int]): 超时时间 / Timeout
            
        Returns:
            Dict[str, Any]: 命令执行结果 / Command execution result
        """
        # 参数验证 / Parameter validation
        if not device_id or not isinstance(device_id, str):
            return self._create_command_result(
                "invalid", CommandStatus.FAILED, 
                "无效的设备ID / Invalid device ID"
            )
        
        if not isinstance(command_data, dict):
            return self._create_command_result(
                "invalid", CommandStatus.FAILED, 
                "无效的命令数据 / Invalid command data"
            )
        
        # 检查队列大小限制 / Check queue size limit
        if len(self.command_queue) >= self.max_queue_size:
            return self._create_command_result(
                "queue_full", CommandStatus.FAILED, 
                "命令队列已满 / Command queue is full"
            )
        
        command_id = self._generate_command_id(device_id)
        start_time = datetime.now()
        
        try:
            self.logger.info(f"发送命令 / Sending command: {command_id} to {device_id} ({command_type.value})")
            
            # 获取设备实例 / Get device instance
            device = device_manager.get_device(device_id)
            if not device:
                self.command_stats['failed_commands'] += 1
                return self._create_command_result(command_id, CommandStatus.FAILED, 
                                                 "设备未找到 / Device not found")
            
            # 创建命令记录 / Create command record
            execution_timeout = timeout or self.default_timeout
            command_record = {
                'command_id': command_id,
                'device_id': device_id,
                'command_type': command_type.value,
                'command_data': command_data,
                'status': CommandStatus.PENDING.value,
                'created_time': start_time,
                'timeout': execution_timeout
            }
            
            # 添加到命令队列 / Add to command queue
            self.command_queue[command_id] = command_record
            
            # 更新统计信息 / Update statistics
            self.command_stats['total_commands'] += 1
            
            # 执行命令 / Execute command
            command_record['status'] = CommandStatus.EXECUTING.value
            
            try:
                result = await safe_execute(
                    device.send_command,
                    command_data,
                    max_attempts=2,
                    circuit_breaker_name=f"command_sending_{device_id}"
                )
            except asyncio.TimeoutError:
                command_record['status'] = CommandStatus.TIMEOUT.value
                command_record['error'] = "命令执行超时 / Command execution timeout"
                self.command_stats['timeout_commands'] += 1
                self._move_to_history(command_id)
                return self._create_command_result(command_id, CommandStatus.TIMEOUT, 
                                                  "命令执行超时 / Command execution timeout")
            except Exception as exec_error:
                command_record['status'] = CommandStatus.FAILED.value
                command_record['error'] = str(exec_error)
                self.command_stats['failed_commands'] += 1
                self._move_to_history(command_id)
                return self._create_command_result(command_id, CommandStatus.FAILED, 
                                                  f"命令执行失败 / Command execution failed: {str(exec_error)}")
            
            # 处理执行结果 / Process execution result
            if result and result.get('status') == 1:
                command_record['status'] = CommandStatus.SUCCESS.value
                self.command_stats['successful_commands'] += 1
            else:
                command_record['status'] = CommandStatus.FAILED.value
                self.command_stats['failed_commands'] += 1
                command_record['error'] = result.get('message', '未知错误 / Unknown error') if result else '无返回结果 / No return result'
            
            command_record['result'] = result
            command_record['completed_time'] = datetime.now()
            command_record['execution_time'] = (command_record['completed_time'] - start_time).total_seconds()
            
            # 移动到历史记录 / Move to history
            self._move_to_history(command_id)
            
            self.logger.info(f"命令执行完成 / Command execution completed: {command_id} ({command_record['status']})")
            
            return self._create_command_result(
                command_id, 
                CommandStatus(command_record['status']), 
                "命令执行完成 / Command execution completed",
                result,
                command_record['execution_time']
            )
            
        except asyncio.TimeoutError:
            self.logger.error(f"命令执行超时 / Command execution timeout: {command_id}")
            self.command_stats['timeout_commands'] += 1
            if command_id in self.command_queue:
                self.command_queue[command_id]['status'] = CommandStatus.TIMEOUT.value
                self._move_to_history(command_id)
            return self._create_command_result(command_id, CommandStatus.TIMEOUT, "命令执行超时 / Command execution timeout")
            
        except Exception as e:
            self.logger.error(f"命令执行异常 / Command execution error: {command_id}, {str(e)}")
            self.command_stats['failed_commands'] += 1
            if command_id in self.command_queue:
                self.command_queue[command_id]['status'] = CommandStatus.FAILED.value
                self.command_queue[command_id]['error'] = str(e)
                self._move_to_history(command_id)
            return self._create_command_result(command_id, CommandStatus.FAILED, f"命令执行异常 / Command execution error: {str(e)}")
    
    async def batch_send_command(self, device_ids: List[str], command_type: CommandType, 
                                command_data: Dict[str, Any], timeout: Optional[int] = None) -> Dict[str, Any]:
        """
        批量发送命令
        Batch send command
        
        Args:
            device_ids (List[str]): 设备ID列表 / Device ID list
            command_type (CommandType): 命令类型 / Command type
            command_data (Dict[str, Any]): 命令数据 / Command data
            timeout (Optional[int]): 超时时间 / Timeout
            
        Returns:
            Dict[str, Any]: 批量命令执行结果 / Batch command execution result
        """
        # 参数验证 / Parameter validation
        if not device_ids or not isinstance(device_ids, list):
            return {
                'total': 0,
                'successful': 0,
                'failed': 0,
                'timeout': 0,
                'error': '设备ID列表为空或无效 / Device ID list is empty or invalid',
                'results': []
            }
        
        if len(device_ids) > 1000:  # 限制批量大小 / Limit batch size
            return {
                'total': len(device_ids),
                'successful': 0,
                'failed': len(device_ids),
                'timeout': 0,
                'error': '批量大小超过限制 / Batch size exceeds limit (1000)',
                'results': []
            }
        
        try:
            self.logger.info(f"批量发送命令 / Batch sending command: {command_type.value} to {len(device_ids)} devices")
            
            # 限制并发数量以避免资源耗尽 / Limit concurrency to avoid resource exhaustion
            max_concurrent = min(50, len(device_ids))
            semaphore = asyncio.Semaphore(max_concurrent)
            
            async def send_single_command(device_id: str):
                async with semaphore:
                    return await self.send_command(device_id, command_type, command_data, timeout)
            
            # 创建任务 / Create tasks
            tasks = [send_single_command(device_id) for device_id in device_ids]
            
            # 等待所有命令完成 / Wait for all commands to complete
            results = await asyncio.gather(*tasks, return_exceptions=True)
            
            # 统计结果 / Count results
            successful = 0
            failed = 0
            timeout_count = 0
            command_results = []
            
            for i, result in enumerate(results):
                if isinstance(result, Exception):
                    failed += 1
                    command_results.append({
                        'device_id': device_ids[i] if i < len(device_ids) else 'unknown',
                        'status': CommandStatus.FAILED.value,
                        'error': str(result)
                    })
                else:
                    # 安全获取结果状态 / Safely get result status
                    status = result.get('status') if isinstance(result, dict) else CommandStatus.FAILED.value
                    
                    if status == CommandStatus.SUCCESS.value:
                        successful += 1
                    elif status == CommandStatus.TIMEOUT.value:
                        timeout_count += 1
                    else:
                        failed += 1
                    
                    # 确保结果包含设备ID / Ensure result contains device ID
                    if isinstance(result, dict) and 'device_id' not in result:
                        result['device_id'] = device_ids[i] if i < len(device_ids) else 'unknown'
                    
                    command_results.append(result)
            
            self.logger.info(
                f"批量命令完成 / Batch command completed: {successful}/{len(device_ids)} successful, "
                f"{failed} failed, {timeout_count} timeout"
            )
            
            return {
                'total': len(device_ids),
                'successful': successful,
                'failed': failed,
                'timeout': timeout_count,
                'results': command_results,
                'timestamp': datetime.now().isoformat()
            }
            
        except Exception as e:
            self.logger.error(f"批量命令发送异常 / Batch command sending error: {str(e)}")
            return {
                'total': len(device_ids),
                'successful': 0,
                'failed': len(device_ids),
                'timeout': 0,
                'error': str(e),
                'results': []
            }
    
    async def send_location_request(self, device_id: str) -> Dict[str, Any]:
        """
        发送定位请求
        Send location request
        
        Args:
            device_id (str): 设备ID / Device ID
            
        Returns:
            Dict[str, Any]: 命令执行结果 / Command execution result
        """
        command_data = {
            "url": "/sendDeviceInstructions",
            "body": {
                "imei": device_id,
                "command": 128  # L16定位命令 / L16 location command
            }
        }
        
        return await self.send_command(device_id, CommandType.LOCATION_REQUEST, command_data)
    
    async def send_message_to_device(self, device_id: str, message: str, voice_type: Optional[str] = None) -> Dict[str, Any]:
        """
        向设备发送消息
        Send message to device
        
        Args:
            device_id (str): 设备ID / Device ID
            message (str): 消息内容 / Message content
            voice_type (Optional[str]): 语音类型 / Voice type
            
        Returns:
            Dict[str, Any]: 命令执行结果 / Command execution result
        """
        command_data = {
            "url": "/sendDeviceInstructions",
            "body": {
                "imei": device_id,
                "type": 0,  # 文本转语音 / Text to speech
                "content": message,
                "voice": voice_type
            }
        }
        
        return await self.send_command(device_id, CommandType.SEND_MESSAGE, command_data)
    
    async def update_device_config(self, device_id: str, config: Dict[str, Any]) -> Dict[str, Any]:
        """
        更新设备配置
        Update device configuration
        
        Args:
            device_id (str): 设备ID / Device ID
            config (Dict[str, Any]): 配置数据 / Configuration data
            
        Returns:
            Dict[str, Any]: 命令执行结果 / Command execution result
        """
        command_data = {
            "url": "/sendDeviceInstructions",
            "body": {
                "imei": device_id,
                **config
            }
        }
        
        return await self.send_command(device_id, CommandType.DEVICE_CONFIG, command_data)
    
    def get_command_status(self, command_id: str) -> Optional[Dict[str, Any]]:
        """
        获取命令状态
        Get command status
        
        Args:
            command_id (str): 命令ID / Command ID
            
        Returns:
            Optional[Dict[str, Any]]: 命令状态 / Command status
        """
        # 先查队列 / Check queue first
        if command_id in self.command_queue:
            return self.command_queue[command_id].copy()
        
        # 再查历史 / Then check history
        for record in self.command_history:
            if record['command_id'] == command_id:
                return record.copy()
        
        return None
    
    def get_device_command_history(self, device_id: str, limit: int = 10) -> List[Dict[str, Any]]:
        """
        获取设备命令历史
        Get device command history
        
        Args:
            device_id (str): 设备ID / Device ID
            limit (int): 限制数量 / Limit count
            
        Returns:
            List[Dict[str, Any]]: 命令历史 / Command history
        """
        device_history = [
            record for record in self.command_history
            if record['device_id'] == device_id
        ]
        
        # 按时间倒序排列 / Sort by time in descending order
        device_history.sort(key=lambda x: x['created_time'], reverse=True)
        
        return device_history[:limit]
    
    def get_command_statistics(self) -> Dict[str, Any]:
        """
        获取命令统计信息
        Get command statistics
        
        Returns:
            Dict[str, Any]: 统计信息 / Statistics information
        """
        stats = self.command_stats.copy()
        
        # 计算成功率 / Calculate success rate
        if stats['total_commands'] > 0:
            stats['success_rate'] = round(stats['successful_commands'] / stats['total_commands'] * 100, 2)
        else:
            stats['success_rate'] = 0
        
        # 添加队列状态 / Add queue status
        stats['pending_commands'] = len(self.command_queue)
        stats['history_size'] = len(self.command_history)
        
        return stats
    
    def _generate_command_id(self, device_id: str) -> str:
        """
        生成命令ID
        Generate command ID
        
        Args:
            device_id (str): 设备ID / Device ID
            
        Returns:
            str: 命令ID / Command ID
        """
        timestamp = int(datetime.now().timestamp() * 1000)
        return f"cmd_{device_id}_{timestamp}"
    
    def _create_command_result(self, command_id: str, status: CommandStatus, 
                              message: str, result: Any = None, execution_time: float = None) -> Dict[str, Any]:
        """
        创建命令结果
        Create command result
        
        Args:
            command_id (str): 命令ID / Command ID
            status (CommandStatus): 命令状态 / Command status
            message (str): 消息 / Message
            result (Any): 执行结果 / Execution result
            execution_time (float): 执行时间 / Execution time
            
        Returns:
            Dict[str, Any]: 命令结果 / Command result
        """
        command_result = {
            'command_id': command_id,
            'status': status.value,
            'message': message,
            'timestamp': datetime.now().isoformat()
        }
        
        if result is not None:
            command_result['result'] = result
        
        if execution_time is not None:
            command_result['execution_time'] = execution_time
        
        return command_result
    
    def _move_to_history(self, command_id: str):
        """
        将命令移动到历史记录
        Move command to history
        
        Args:
            command_id (str): 命令ID / Command ID
        """
        try:
            if command_id in self.command_queue:
                command_record = self.command_queue.pop(command_id)
                
                # 添加到历史记录 / Add to history
                self.command_history.append(command_record)
                
                # 限制历史记录大小 / Limit history size
                if len(self.command_history) > self.max_history_size:
                    # 保留最新的记录 / Keep the latest records
                    self.command_history = self.command_history[-self.max_history_size:]
                    
        except Exception as e:
            self.logger.error(f"移动命令到历史记录失败 / Failed to move command to history: {command_id}, {str(e)}")
    
    async def cancel_command(self, command_id: str) -> bool:
        """
        取消命令
        Cancel command
        
        Args:
            command_id (str): 命令ID / Command ID
            
        Returns:
            bool: 是否成功取消 / Whether successfully cancelled
        """
        if command_id in self.command_queue:
            command_record = self.command_queue[command_id]
            if command_record['status'] == CommandStatus.PENDING.value:
                command_record['status'] = CommandStatus.CANCELLED.value
                command_record['completed_time'] = datetime.now()
                self._move_to_history(command_id)
                self.logger.info(f"命令已取消 / Command cancelled: {command_id}")
                return True
        
        return False
    
    async def cleanup_history(self, days: int = 7):
        """
        清理历史记录
        Cleanup history
        
        Args:
            days (int): 保留天数 / Days to keep
        """
        try:
            if days <= 0:
                self.logger.warning("无效的保留天数，跳过清理 / Invalid days to keep, skipping cleanup")
                return
                
            cutoff_time = datetime.now() - timedelta(days=days)
            original_count = len(self.command_history)
            
            # 安全地清理历史记录 / Safely cleanup history records
            cleaned_history = []
            for record in self.command_history:
                try:
                    # 检查记录是否有效 / Check if record is valid
                    if isinstance(record, dict) and 'created_time' in record:
                        created_time = record['created_time']
                        if isinstance(created_time, datetime) and created_time > cutoff_time:
                            cleaned_history.append(record)
                    else:
                        # 保留无效记录以避免数据丢失 / Keep invalid records to avoid data loss
                        cleaned_history.append(record)
                except Exception as record_error:
                    self.logger.warning(f"处理历史记录失败，保留记录 / Failed to process history record, keeping it: {str(record_error)}")
                    cleaned_history.append(record)
            
            self.command_history = cleaned_history
            
            cleaned_count = original_count - len(self.command_history)
            if cleaned_count > 0:
                self.logger.info(
                    f"清理了 {cleaned_count} 条历史记录 / Cleaned up {cleaned_count} history records, "
                    f"保留 {len(self.command_history)} 条 / Kept {len(self.command_history)} records"
                )
            else:
                self.logger.info("没有需要清理的历史记录 / No history records need cleanup")
                
        except Exception as e:
            self.logger.error(f"清理历史记录失败 / Failed to cleanup history records: {str(e)}")


# 全局命令处理器实例 / Global command handler instance
# 使用延迟初始化以避免循环导入 / Use lazy initialization to avoid circular imports
_command_handler_instance = None

def get_command_handler() -> CommandHandler:
    """获取命令处理器实例 / Get command handler instance"""
    global _command_handler_instance
    if _command_handler_instance is None:
        _command_handler_instance = CommandHandler()
    return _command_handler_instance

# 为了向后兼容，保留原有的变量名 / For backward compatibility, keep the original variable name
command_handler = get_command_handler()